#-*-coding:gb2312-*-
###############################################################
## Name      : gen_xml_trans
## Author     : gaojiaming
## Date       : Tue Sep 20 16:39:36 CST 2022
## Description:
##
##
###############################################################
import openpyxl
import sys
import os
import re
import argparse
import subprocess
import time
import urllib.request
import urllib.parse
import json

reg_xlsx_path = "D:\MyWork\gitee_work\gen-reg-rtl-ral\demo\REG_PRJ.xlsx"

class Field:
	def __init__(self, name, offset, width, access, default, description):
		self.name = name
		self.offset = offset
		self.width = width
		self.access = access
		self.default = default
		self.description = description
	def __str__(self, head = "    "):
		str = head + "Field Info:\n"
		str = str + head + "    name     : %s\n" % self.name
		str = str + head + "    offset   : %s\n" % self.offset
		str = str + head + "    width    : %s\n" % self.width
		str = str + head + "    access   : %s\n" % self.access
		str = str + head + "    default  : %s\n" % self.default
		str = str + head + "    description: %s\n" % self.translate()
		return str
	def add_reg(self, reg):
		self.reg = reg
	def translate(self):
		self.pre_tras = re.sub(r"\n", "   ", self.description)
		ch_flag = 0
		for ch in self.pre_tras:
			if u'\u4e00' <= ch <= u'\u9fff':
				ch_flag = 1
				break
		if ch_flag == 1:
			str = ""
			#str = translate(self.pre_tras)
			#str = self.pre_tras
			#str = translate_other(self.pre_tras)
		else:
			str = self.pre_tras
		return str+"\n"
	def gen_xml(self, head_in = "          "):
		head = head_in + "  "
		str  = ""
		str += head_in + "<spirit:field>\n"
		str += head + "<spirit:name>{0}</spirit:name>\n".format(self.name)
		str += head + "<spirit:description>\n"
		str += head + self.translate()
		str += head + "</spirit:description>\n"
		str += head + "<spirit:bitOffset>{0}</spirit:bitOffset>\n".format(self.offset)
		str += head + "<spirit:bitWidth>{0}</spirit:bitWidth>\n".format(self.width)
		str += head + "<spirit:volatile>true</spirit:volatile>\n"
		str += head + "<spirit:resets>\n"
		str += head + "<spirit:reset>\n"
		str += head + "  <spirit:value>{0}</spirit:value>\n".format(self.default)
		str += head + "</spirit:reset>\n"
		str += head + "</spirit:resets>\n"
		if self.access == "RW":
			str += head + "<spirit:access>read-write</spirit:access>\n"
		elif self.access == "W1C":
			str += head + "<spirit:access>read-write</spirit:access>\n"
			str += head + "<spirit:modifiedWriteValue>oneToClear</spirit:modifiedWriteValue>\n"
		elif self.access == "RO":
			str += head + "<spirit:access>read-only</spirit:access>\n"
		elif self.access == "RC":
			str += head + "<spirit:access>read-only</spirit:access>\n"
			str += head + "<spirit:readAction>clear</spirit:readAction>\n"
		elif self.access == "WO":
			tr += head + "<spirit:access>write-only</spirit:access>\n"
		str += head_in + "</spirit:field>\n"
		return str

class Register:
	def __init__(self, name, addr, size = 32):
		self.name = name
		self.addr = addr
		self.size = size
		self.repeat_times = 1
		self.repeat_flag = 0
		self.field_list = []
	def __str__(self, head = ""):
		str = head + "Register Info:\n"
		str = str + head + "    name      : %s\n" % self.name
		str = str + head + "    addr      : %s\n" % self.addr
		str = str + head + "    size      : %s\n" % self.size
		if self.repeat_times != 1:
			str = str + head + "    repeat     : %s\n" % self.repeat_times
		for field in self.field_list:
			str += field.__str__(head+"    ")
		return str
	def copy(self, name, addr):
		to = Register(name, addr)
		to.field_list = self.field_list
		return to
	def add_field(self, field):
		self.field_list.append(field)
	def reg_repeat(self, times):
		self.repeat_times = times
	def update_name(self, name):
		self.name = name
	def update_addr(self, addr):
		self.addr = addr
	def gen_xml(self, head_in = "        "):
		head = head_in + "  "
		str  = ""
		str += head_in + "<spirit:register>\n"
		str += head + "<spirit:name>{0}</spirit:name>\n".format(self.name)
		str += head + "<spirit:displayName>{0}</spirit:displayName>\n".format(self.name)
		str += head + "<spirit:addressOffset>{0}</spirit:addressOffset>\n".format(self.addr)
		str += head + "<spirit:size>32</spirit:size>\n"
		str += head + "<spirit:reset>\n"
		str += head + "  <spirit:mask>0xFFFFFFFF</spirit:mask>\n"
		str += head + "</spirit:reset>\n"
		for field in self.field_list:
			str += field.gen_xml()
		str += head_in + "</spirit:register>\n"
		return str

class Block:
	def __init__(self, name, offset, ranges):
		self.name = name
		self.offset = offset
		self.ranges = ranges
		self.reg_list = []
	def __str__(self, head = ""):
		str = head + "Block Info:\n"
		str = str + head + "    name      : %s\n" % self.name
		str = str + head + "    offset    : %s\n" % self.offset
		str = str + head + "    ranges    : %s\n" % self.ranges
		for reg in self.reg_list:
			str = str + reg.__str__("    ")
		return str
	def gen_xml(self, head_in = "      "):
		head = head_in + "  "
		str  = ""
		str += head_in + "<spirit:addressBlock>\n"
		str += head + "<spirit:name>{0}</spirit:name>\n".format(self.name)
		str += head + "<spirit:baseAddress>{0}</spirit:baseAddress>\n".format(self.offset)
		str += head + "<spirit:range>{0}</spirit:range>\n".format(self.ranges)
		str += head + "<spirit:width>32</spirit:width>\n"
		str += head + "<spirit:usage>register</spirit:usage>\n"
		for reg in self.reg_list:
			if reg.repeat_times == 1:
				str += reg.gen_xml()
			else:
				base_addr = int(reg.addr, 16)
				base_name = reg.name
				for i in range(0, reg.repeat_times):
					addr = base_addr + i*4
					name = "{0}{1}".format(base_name, i)
					reg.update_addr(hex(addr))
					reg.update_name(name)
					str += reg.gen_xml()
				reg.update_addr(hex(base_addr))
				reg.update_name(base_name)
		str += head_in + "</spirit:addressBlock>\n"
		return str

def input_args_proc():#{{{
	global xlsx
	parser = argparse.ArgumentParser(description="argparse info")
	parser.add_argument('-e', help='reg.xlsx')
	result = parser.parse_args()
	xlsx = result.e
pass#}}}

def translate(line):
	req_url = 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
	Form_Date = {}
	Form_Date['i'] = line
	Form_Date['doctype'] = 'json'
	Form_Date['form'] = 'AUTO'
	Form_Date['to'] = 'AUTO'
	Form_Date['smartresult'] = 'dict'
	Form_Date['client'] = 'fanyideskweb'
	Form_Date['salt'] = '1526995097962'
	Form_Date['sign'] = '8e4c4765b52229e1f3ad2e633af89c76'
	Form_Date['version'] = '2.1'
	Form_Date['keyform'] = 'fanyi.web'
	Form_Date['action'] = 'FY_BY_REALTIME'
	Form_Date['typoResult'] = 'false'

	data = urllib.parse.urlencode(Form_Date).encode('utf-8')
	response = urllib.request.urlopen(req_url, data)
	html = response.read().decode('utf-8')
	translate_results = json.loads(html)
	translate_results = translate_results['translateResult'][0][0]['tgt']
	#translate_results = translate_results['translateResult'][0][0]
	return translate_results

def translate_other(line):
	data = {'i': line,
				'from': 'AUTO',
				'to': 'AUTO', 
				'smartresult': 'dict',
				'client': 'fanyideskweb',
				'salt': '',
				'sign': '',
				'doctype': 'json',
				'version': '2.1',
				'keyfrom': 'fanyi.web',
				'action': 'FY_BY_REALTIME',
				'typoResult': 'false'}
	url = 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
	data = urllib.parse.urlencode(data).encode('utf-8')
	r = urllib.request.urlopen(url, data)
	html = r.read().decode('utf-8')
	target = json.loads(html)
	translate_results = target['translateResult'][0][0]['tgt']
	return translate_results

def read_xlsx(xlsx):#{{{
	global sys_name
	global root_path
	print("Read " + xlsx)
	path, fl = os.path.split(xlsx)
	root_path = path
	sys_name, unuse = os.path.splitext(fl)
	
	wb = openpyxl.load_workbook(xlsx)
	sheetnames = wb.sheetnames
	for name in sheetnames:
		if name != "readme":
			ws = wb[name]
			read_block_sheet(ws)
		else:
			ws = wb[name]
			read_top_sheet(ws)
	for name in block_dict:
		#print(block_dict[name])
		pass
#}}}

def read_top_sheet(ws):#{{{
	global block_dict
	global block_list
	global gen_list
	gen_list = []
	block_dict = {}
	block_list = []
	for row in range(1, ws.max_row):
		cell_list = list(ws.rows)[row]
		name = cell_list[0].value
		offset = cell_list[1].value
		ranges = cell_list[2].value
		print("  readme add block " + name + " in list!")
		block = Block(name, offset, ranges)
		block_dict[name] = block
		block_list.append(name)

def read_block_sheet(ws):#{{{
	reg_list = block_dict[ws.title].reg_list
	gen_list.append(ws.title)
	reg_addr = ""
	reg_name = ""
	field_offset = 0
	#print(ws.title, ws.max_row)
	for row in range(1, ws.max_row):
		cell_list = list(ws.rows)[row]
		if cell_list[2].value != None: #new field
			
			if cell_list[0].value != None: #new reg
				if len(reg_list) > 0 and reg_list[-1].repeat_times > 1: #repeat regs
					reg = reg_list.pop()
					#print(reg)
					for i in range(0, reg.repeat_times):
						reg_name = reg.name + str(i)
						reg_addr = hex(int(reg.addr, 16) + i*4)
						#print(i, reg_addr, reg_name)
						repeat_reg = reg.copy(reg_name, reg_addr)
						repeat_reg.repeat_flag = 1
						reg_list.append(repeat_reg)
				
				reg_addr = cell_list[0].value
				reg_name = cell_list[1].value
				field_offset = 0
				reg = Register(reg_name, reg_addr)
				res_name = re.search(r"(\w+)\{n\},\s*n = 0~(\d+)", reg_name)
				if res_name: #repeat
					reg_addr   = re.search(r"(0x\w+)~(0x\w+)", reg_addr).group(1)
					reg_name = res_name.group(1)
					reg = Register(reg_name, reg_addr)
					reg.reg_repeat(int(res_name.group(2))+1)
					#print(row, cell_list[2].value, reg_name)
				else: #normal
					reg = Register(reg_name, reg_addr)
				reg_list.append(reg)
			#END: if cell_list[0].value != None: #new reg
			
			field_name = cell_list[2].value.strip()
			field_width = cell_list[3].value
			field_access = cell_list[4].value.strip()
			field_default = cell_list[5].value.strip()
			field_description = cell_list[6].value
			field = Field(field_name, field_offset, field_width, field_access, field_default, field_description)
			reg_list[-1].add_field(field)
			field_offset += field_width
	if len(reg_list) > 0 and reg_list[-1].repeat_times > 1: 
		#print("need add " + reg_list[-1].name)
		reg = reg_list.pop()
		for i in range(0, reg.repeat_times):
			reg_name = reg.name + str(i)
			reg_addr = hex(int(reg.addr, 16) + i*4)
			#print(i, reg_addr, reg_name)
			repeat_reg = reg.copy(reg_name, reg_addr)
			repeat_reg.repeat_flag = 1
			reg_list.append(repeat_reg)
	for reg in reg_list:
		#print(reg)
		pass
#}}}

def gen_xml():
	#print("Gen xml")
	head = '''<?xml version="1.0" encoding="UTF-8"?>
<spirit:component xmlns:spirit="http://www.spiritconsortium.org/XMLSchema/SPIRIT/1685-2009" >
  <spirit:vendor></spirit:vendor>
  <spirit:library></spirit:library>
  <spirit:version>1.0</spirit:version>\n'''
  
	str  = head
	str += "  <spirit:name>{0}_Description</spirit:name>\n".format(sys_name)
	str += "  <spirit:memoryMaps>\n"
	str += "    <spirit:memoryMap>\n"
	str += "      <spirit:name>{0}</spirit:name>\n".format(sys_name)
	
	for block_name in gen_list:
		block = block_dict[block_name]
		str += block.gen_xml()
	str += '''    </spirit:memoryMap>
  </spirit:memoryMaps>
</spirit:component>'''
	return str

def debug_log():
	str = ""
	for block_name in gen_list:
		str += "BLOCK: " + block_name + "\n"
		block = block_dict[block_name]
		str += block.__str__()
	return str

def gen_inf_trans():
	global output_list
	global input_list
	output_list = []
	input_list = []
	assign_list = []
	dff_list = []
	
	for block_name in gen_list:
		blk_inf_name = "ral_blk_{0}_{1}_inf".format(sys_name, block_name)
		blk_inf_inst = "{0}_usr".format(block_name)
		block = block_dict[block_name]
		for reg in block.reg_list:
			for field in reg.field_list:
				field_name = field.name
				wire_name = "{0}_{1}".format(block_name, field_name)
				if reg.repeat_flag == 1:
					field_name = "{0}_{1}".format(reg.name, field.name)
					wire_name = "{0}_{1}".format(block_name, field_name)
				elif field_name == reg.name:
					field_name = "{0}_{1}".format(reg.name, field.name)
					wire_name = "{0}_{1}".format(block_name, field.name)
					
				if field.access == "RW":
					if field.width != 1:
						output_list.append("output [{0} -1:0]{1},".format(field.width, wire_name))
					else:
						output_list.append("output {1},".format(field.width, wire_name))
					assign_list.append("assign {0} = {1}.{2}_out;".format(wire_name, blk_inf_inst, field_name))
					if field.name == "spu_proc_start" or field.name == "soft_rst_en":
						assign_list.append("assign {1}.{0}_wen = {2}_wen;".format(field_name, blk_inf_inst, wire_name))
						assign_list.append("assign {1}.{0}_in  = {2}_in;".format(field_name, blk_inf_inst, wire_name))
					else:
						assign_list.append("assign {1}.{0}_wen = '0;".format(field_name, blk_inf_inst))
						assign_list.append("assign {1}.{0}_in = '0;".format(field_name, blk_inf_inst))
				elif field.access == "W1C":
					if field.width != 1:
						input_list.append("input [{0} -1:0]{1}_in,".format(field.width, wire_name))
						input_list.append("input {1}_wen,".format(field.width, wire_name))
						dff_list.append("wire [{0} -1:0]{1}_ff;".format(field.width, wire_name))
					else:
						input_list.append("input {1}_in,".format(field.width, wire_name))
						input_list.append("input {1}_wen,".format(field.width, wire_name))
						dff_list.append("wire {1}_ff;".format(field.width, wire_name))
					assign_list.append("assign {1}.{0}_wen = {2}_wen_ff;".format(field_name, blk_inf_inst, wire_name))
					assign_list.append("assign {1}.{0}_in  = {2}_in_ff;".format(field_name, blk_inf_inst, wire_name))
					dff_list.append("reg_dffr #(.WIDTH({0})) u_{1}_in(clk, rst_n, {1}_in, {1}_in_ff);".format(field.width, wire_name))
					dff_list.append("reg_dffr #(.WIDTH(1)) u_{1}_wen(clk, rst_n, {1}_wen, {1}_wen_ff);".format(field.width, wire_name))
					dff_list.append("")
				elif field.access == "RO":
					if field.width != 1:
						input_list.append("input [{0} -1:0]{1},".format(field.width, wire_name))
					else:
						input_list.append("input {1},".format(field.width, wire_name))
					assign_list.append("assign {1}.{0}_in = {2}_ff;".format(field_name, blk_inf_inst, wire_name))
					dff_list.append("wire [{0} -1:0]{1}_ff;".format(field.width, wire_name))
					dff_list.append("reg_dffr #(.WIDTH({0})) u_{1}(clk, rst_n, {1}, {1}_ff);".format(field.width, wire_name))
					dff_list.append("")
		assign_list.append("")

	str = ""
	str += "module {0}_reg_trans(".format(sys_name) + "\n"
	str += "    " + "input clk," + "\n"
	str += "    " + "input rst_n," + "\n"
	for line in output_list:
		str += "    " + line + "\n"
	str += "\n"
	for line in input_list:
		str += "    " + line + "\n"
	str += "\n"
	for block_name in gen_list:
		if block_name == "core_cfg":
			continue
		blk_inf_name = "ral_blk_{0}_{1}_itf".format(sys_name, block_name)
		blk_inf_inst = "{0}_usr".format(block_name)
		str += "    " + blk_inf_name + ".usr " + blk_inf_inst + ",\n"
	str = re.sub(r",\n$", r"\n", str)
	str += ");\n\n"
	
	for line in dff_list:
		str += "    " + line + "\n"
	str += "\n"
	for line in assign_list:
		str += "    " + line + "\n"
	str += "\n"
	str += "endmodule"
	return str

def gen_reg_rtl():
	str  = "module {}_reg_rtl(\n".format(sys_name)
	for block_name in gen_list:
		str += "    ral_blk_{0}_{1}_itf.regs {1}_regs,\n".format(sys_name, block_name)

	str += '''
    input                                 pclk,
    input                                 presetn, 
    input   [`VMM_RAL_ADDR_WIDTH     -1:0]paddr,
    input                                 psel,
    input                                 penable,
    input                                 pwrite,
    input   [`VMM_RAL_DATA_WIDTH     -1:0]pwdata,
    input   [(`VMM_RAL_DATA_WIDTH/8) -1:0]pstrb,
    output                                pready,
    output  [`VMM_RAL_DATA_WIDTH     -1:0]prdata,
    output                                pslverr
);
'''

	str += '''
vmm_ral_host_itf u_host_mst(pclk, presetn);

apb2hostif
u_apb2hostif(
             // Interfaces
             .mst                       (u_host_mst.master),
             // Outputs
             .pready                    (pready),
             .prdata                    (prdata[`VMM_RAL_DATA_WIDTH-1:0]),
             .pslverr                   (pslverr),
             // Inputs
             .pclk                      (pclk),
             .presetn                   (presetn),
             .paddr                     (paddr[`VMM_RAL_ADDR_WIDTH-1:0]),
             .psel                      (psel),
             .penable                   (penable),
             .pwrite                    (pwrite),
             .pwdata                    (pwdata[`VMM_RAL_DATA_WIDTH-1:0]),
             .pstrb                     (pstrb[(`VMM_RAL_DATA_WIDTH/8)-1:0]));
             
'''

	for block_name in gen_list:
		str += "vmm_ral_host_itf u_{0}_mst(pclk, presetn);\n".format(block_name)
	str += "\n"

	str += "ral_sys_{0}_rtl\n".format(sys_name)
	str += "u_sys_{0}(\n".format(sys_name)
	for block_name in gen_list:
		str += "    .{0}(u_{0}_mst.master),\n".format(block_name)
	str += "    .hst(u_host_mst.slave)\n"
	str += ");\n\n"
	
	for block_name in gen_list:
		str += "ral_blk_{0}_{1}_rtl u_blk_{0}_{1}(.hst(u_{1}_mst.slave), .usr({1}_regs));\n".format(sys_name, block_name)
	str += "\n"

	str += "endmodule"
	return str

def gen_reg_top():
	str = "module {0}_reg_top(\n".format(sys_name)
	for line in output_list:
		str += "    " + line + "\n"
	str += "\n"
	for line in input_list:
		str += "    " + line + "\n"
	str += "\n"
		
	str += '''
    input                                 pclk,
    input                                 presetn, 
    input   [`VMM_RAL_ADDR_WIDTH     -1:0]paddr,
    input                                 psel,
    input                                 penable,
    input                                 pwrite,
    input   [`VMM_RAL_DATA_WIDTH     -1:0]pwdata,
    input   [(`VMM_RAL_DATA_WIDTH/8) -1:0]pstrb,
    output                                pready,
    output  [`VMM_RAL_DATA_WIDTH     -1:0]prdata,
    output                                pslverr
);

'''	
	for block_name in gen_list:
		str += "ral_blk_{0}_{1}_itf u_{1}_itf();\n".format(sys_name, block_name)
	str += "\n"
	
	str += "{0}_reg_rtl u_reg(\n".format(sys_name)
	for block_name in gen_list:
		str += "    .{0}_regs(u_{0}_itf.regs),\n".format(block_name)

	str +='''
    // Outputs
    .pready(pready),
    .prdata(prdata[`VMM_RAL_DATA_WIDTH-1:0]),
    .pslverr(pslverr),
    // Inputs
    .pclk(pclk),
    .presetn(presetn),
    .paddr(paddr[`VMM_RAL_ADDR_WIDTH-1:0]),
    .psel(psel),
    .penable(penable),
    .pwrite(pwrite),
    .pwdata(pwdata[`VMM_RAL_DATA_WIDTH-1:0]),
    .pstrb(pstrb[(`VMM_RAL_DATA_WIDTH/8)-1:0]));
    
'''
	str += "{0}_reg_trans u_trans(\n".format(sys_name)
	str += "    .clk(pclk),\n"
	str += "    .rst_n(presetn),\n"
	str += "    //output\n"
	for line in output_list:
		sig = re.search(r"output\s.*?(\w+),", line).group(1)
		str += "    .{0}({0}),\n".format(sig)
	str += "    //input\n"
	for line in input_list:
		sig = re.search(r"input\s.*?(\w+),", line).group(1)
		str += "    .{0}({0}),\n".format(sig)
	str += "\n"
	for block_name in gen_list:
		str += "    .{0}_usr(u_{0}_itf.usr),\n".format(block_name)
	#str = str.rstrip()
	str = re.sub(r",\n$", "\n", str)
	str += ");\n\n"
	str += "endmodule"
	return str
	
def main():
	print("START")
	
	read_xlsx(reg_xlsx_path)

	xml_path = "{0}\{1}_Description.xml".format(root_path, sys_name)
	log_path = "{0}\{1}_log.txt".format(root_path, sys_name)
	trans_sv_path = "{0}\{1}_inf_trans.sv".format(root_path, sys_name)
	reg_rtl_sv_path = "{0}\{1}_reg_rtl.sv".format(root_path, sys_name)
	reg_top_sv_path = "{0}\{1}_reg_top.sv".format(root_path, sys_name)

	with open(xml_path, "w") as hd:
		hd.write(gen_xml())
	print("gen ", xml_path)

	with open(log_path, "w") as hd:
		hd.write(debug_log())
	print("gen ", log_path)

	with open(trans_sv_path, "w") as hd:
		hd.write(gen_inf_trans())
	print("gen ", trans_sv_path)

	with open(reg_rtl_sv_path, "w") as hd:
		hd.write(gen_reg_rtl())
	print("gen ", reg_rtl_sv_path)

	with open(reg_top_sv_path, "w") as hd:
		hd.write(gen_reg_top())
	print("gen ", reg_top_sv_path)
		
if __name__ == "__main__":
	main()


